package com.han.http.client.method;

import com.han.http.client.exception.NullContentMatchedException;
import com.han.http.client.exception.RequestNotExecutedException;
import com.han.http.client.paramter.RequestHeader;
import com.han.http.util.Regex;

import java.io.IOException;
import java.net.Proxy;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @fileName: BaseHttpRequest
 * @author: Han
 * @date: 2018/6/21 14:38
 * @dscription: 基础请求抽象类, 定义请求方法类的基本方法, 获取请求方法类
 */
public abstract class BaseHttpRequest {

    /**
     * 获取get方法
     *
     * @param urlStr 地址
     * @return HttpGet
     */
    public static HttpGet getGetMethod(String urlStr) {
        return new HttpGet(urlStr);
    }

    /**
     * 获取Post请求方法
     *
     * @param urlStr url
     * @return HttpPost
     */
    public static HttpPost getPostMethod(String urlStr) {
        return new HttpPost(urlStr);
    }

    /**
     * 执行HttpURLConnection实例化,设置各个属性
     *
     * @throws IOException 抛出异常
     */
    public abstract void execute() throws IOException;

    /**
     * 获取指定key的响应头的值
     *
     * @param key 值
     * @return 值
     * @throws RequestNotExecutedException 没有想执行execute()
     */
    public abstract String getHeaderField(String key) throws RequestNotExecutedException;

    /**
     * 获得全部响应头
     *
     * @return 全部响应头
     * @throws RequestNotExecutedException 没有想执行execute()
     */
    public abstract Map<String, List<String>> getHeaderFields() throws RequestNotExecutedException;

    /**
     * 获取代理
     *
     * @return 代理
     */
    public abstract Proxy getProxy();

    /**
     * 设置代理
     *
     * @param proxy 代理
     */
    public abstract void setProxy(Proxy proxy);

    /**
     * 获取读取超时时间
     *
     * @return 读取超时时间
     */
    public abstract int getReadTimeout();

    /**
     * 设置读取超时时间
     *
     * @param readTimeout 读取超时时间
     */
    public abstract void setReadTimeout(int readTimeout);

    /**
     * 获取连接超时时间
     *
     * @return 连接超时时间
     */
    public abstract int getConnectTimeout();

    /**
     * 设置连接超时时间
     *
     * @param connectTimeout 连接超时时间
     */
    public abstract void setConnectTimeout(int connectTimeout);

    /**
     * 获取是否响应3xx的重定向
     *
     * @return 是否重定向
     */
    public abstract boolean isRedirects();

    /**
     * 设置是否响应3xx的重定向
     *
     * @param redirects 是否重定向
     */
    public abstract void setRedirects(boolean redirects);

    /**
     * 获取请求头
     *
     * @return 请求头
     */
    public abstract RequestHeader[] getHeaders();

    /**
     * 设置请求头
     *
     * @param headers 请求头数组
     */
    public abstract void setHeaders(RequestHeader[] headers);

    /**
     * 设置单个请求头
     *
     * @param header 请求头
     */
    public abstract void setHeader(RequestHeader header);

    /**
     * 使用Gzip解码
     *
     * @param charset 编码方式
     * @return 字符串
     * @throws IOException                 抛出异常
     * @throws RequestNotExecutedException 没有先执行execute()
     */
    abstract String getTextWithGzip(String charset) throws IOException, RequestNotExecutedException;

    /**
     * 不使用Gzip解码
     *
     * @param charset 编码方式
     * @return 字符串
     * @throws IOException                 抛出异常
     * @throws RequestNotExecutedException 没有先执行execute()
     */
    abstract String getTextWithoutGzip(String charset) throws IOException, RequestNotExecutedException;

    /**
     * 读取从页面获取的字符串
     *
     * @return 页面字符串
     * @throws IOException                 抛出异常
     * @throws RequestNotExecutedException 没有先执行execute()
     */
    public String getText() throws IOException, RequestNotExecutedException {

        String text;

        //获取编码方式
        String contentType = getHeaderField("Content-Type");
        String charset;
        try {
            charset = Regex.matchString(contentType, "(?<=charset=)((?!;).)*");
        } catch (NullContentMatchedException | NullPointerException e) {
            charset = "utf-8";
        }
        // TODO: 2018/6/21 这种方式暂时不能确定是否可以准确地获取charset.
        // 1.如果后期遇到乱码,使用一个探针,获取指定位置的字符串是否为确定字符,如果是,即为此种编码方式
        // 2.可以从页面获取charset,虽然这种更不准

        //获取加密方式
        String contentEncoding = getHeaderField("Content-Encoding");
        String gzip = "gzip";
        //根据加密方式读取字符串
        if (!Objects.isNull(contentEncoding) && contentEncoding.contains(gzip)) {
            text = getTextWithGzip(charset);
        } else {
            text = getTextWithoutGzip(charset);
        }

        return text;
    }

    /**
     * 指示近期服务器不太可能有其他请求
     * @throws RequestNotExecutedException 没有先执行execute()
     */
    abstract void disconnect() throws RequestNotExecutedException;

    /**
     * 获取状态码
     * @return 状态码
     * @throws RequestNotExecutedException 没有执行请求
     * @throws IOException IO异常
     */
    abstract int getStatusCode() throws RequestNotExecutedException,IOException;

    /**
     * 获取页面内容的字节数组
     * @return 字节数组
     * @throws IOException IO异常
     * @throws RequestNotExecutedException 没有执行execute()就获取
     */
    abstract byte[] getBytes()throws IOException,RequestNotExecutedException;
}
